﻿using BattleFury.Lib.Lights;
using BattleFury.Lib.Maps;
using BattleFury.Lib.Tiles;
using Cartographer.Generator;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BattleFury.Cartographer.Builder
{
	public static class WorldBuilder
	{
		private static float[,] _heightMapData;
		private static Texture2D _heightMap;
		private static Texture2D _visualizationMap;

		public static GraphicsDevice GraphicsDevice { get; set; }
		public static WorldBuilderSettings Settings { get; set; }

		public static Texture2D HeightMap
		{
			get
			{
				if(GraphicsDevice == null)
					throw new NoSuitableGraphicsDeviceException("No GraphicsDevice found. WorldBuilder not Initialized.");

				if (_heightMap != null) return _heightMap;

				if (_heightMapData == null) BuildHeightMap(Settings.Width, Settings.Height, Settings.Octaves, Settings.Frequency, Settings.Amplitude, Settings.Seed);

				var width = Settings.Width;
				var height = Settings.Height;

				var output = new Texture2D(GraphicsDevice, width, height);

				var pixels = new Color[width * height];
				for (var offset = 0; offset < width * height; offset++)
				{
					var x = offset % width;
					var y = offset / width;
					var h = _heightMapData[x, y];
					var c = new Color(h, h, h, 1f);
					pixels[offset] = c;
				}

				output.SetData(pixels);

				_heightMap = output;
				return output;
			}
		}
		
		static WorldBuilder()
		{
			Settings = new WorldBuilderSettings();
		}

		public static void Initialize(GraphicsDevice graphicsDevice)
		{
			Settings = new WorldBuilderSettings();
			GraphicsDevice = graphicsDevice;
			_heightMap = null;
			_heightMapData = null;
			_visualizationMap = null;
		}

		private static void BuildHeightMap(int width, int height, int octaves, float frequency, float amplitude, int seed)
		{
			_heightMapData = WorldGenerator.GenerateHeightMap(width, height, octaves, frequency, amplitude, seed);
		}

		public static Texture2D BuildVisualizationTexture(VisualizationScale scale)
		{
			var output = new Texture2D(GraphicsDevice, Settings.Width, Settings.Height);

			var width = Settings.Width;
			var height = Settings.Height;
			var pixels = new Color[width * height];
			for (var offset = 0; offset < width*height; offset++)
			{
				var x = offset % width;
				var y = offset / width;
				var data = _heightMapData[x, y] * 255;
				var pixel = new Color(0, 0, 0, 255);
				foreach (var scaleColor in scale)
				{
					if (data <= scaleColor.Max && data > scaleColor.Min) 
						pixel = scaleColor.Color;
				}
				pixels[offset] = pixel;
			}
			output.SetData(pixels);

			_visualizationMap = output;

			return output;
		}

		public static TileMap BuildTileMap()
		{
			var width = Settings.Width;
			var height = Settings.Height;
			var mapSize = new Vector2(width, height);
			var tileSize = new Vector2(32f, 32f);

			// Visualization Data
			var vData = new Color[Settings.Width * Settings.Height];
			_visualizationMap.GetData(vData);

			// Tileset
			var tilesetTexture = new Texture2D(GraphicsDevice, 32, 32);
			var pixels = new Color[32 * 32];
			for (var i = 0; i < 32 * 32; i++)
			{
				pixels[i] = Color.White;
			}
			tilesetTexture.SetData(pixels);
			var tileset = new Tileset(tilesetTexture, tileSize);

			// Tile Map
			var output = new TileMap(mapSize);
			
			// Ground Layer
			var groundLayer = new TileLayer(mapSize, tileSize);
			groundLayer.Name = "Ground";
			
			for (var offset = 0; offset < width*height; offset++)
			{
				var x = offset % width;
				var y = offset / width;
				var tile = new Tile
				{
					Tileset = tileset,
					Size = tileSize,
					Index = 0,
					Position = new Vector2(x * tileSize.X, y * tileSize.Y),
					Tint = vData[offset]
				};
				groundLayer.Tiles[x, y] = tile;
			}
			output.AddLayer(groundLayer);

			// Detail Layer
			var detailLayer = new TileLayer(mapSize, tileSize);
			detailLayer.Name = "Detail";

			for (var offset = 0; offset < width * height; offset++)
			{
				var x = offset % width;
				var y = offset / width;
				var tile = new Tile
				{
					Tileset = tileset,
					Size = tileSize,
					Index = -1,
					Position = new Vector2(x * tileSize.X, y * tileSize.Y),
					Tint = vData[offset]
				};
				detailLayer.Tiles[x, y] = tile;
			}
			output.AddLayer(detailLayer);

			// Collision Layer
			var collisionLayer = new CollisionLayer(mapSize, tileSize);
			collisionLayer.Name = "Collisions";

			for (var offset = 0; offset < width * height; offset++)
			{
				var x = offset % width;
				var y = offset / width;

				CollisionTile tile = null;
				var pixel = vData[offset];
				if (pixel == new Color(0, 0, 128) || pixel == new Color(0, 0, 192) || pixel == new Color(0, 0, 255))
				{
					tile = new CollisionTile();
				}

				collisionLayer.SetTile(x, y, tile);
			}
			output.AddLayer(collisionLayer);

			// Light Layer
			var lightLayer = new LightLayer
			{
				Name = "Lights"
			};
			output.AddLayer(lightLayer);

			return output;
		}
	}
}